package org.apache.ocean.main.name;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.ocean.ClusterConstants;
import org.apache.ocean.main.client.OceanXML;
import org.apache.ocean.main.http.HttpParameters;
import org.apache.ocean.main.server.TransactionServlet.Response;
import org.apache.ocean.main.util.CElement;
import org.apache.ocean.main.util.PrintStringWriter;
import org.apache.ocean.main.util.Util;
import org.apache.ocean.main.util.XMLUtil;
import org.jdom.Element;

public class NameServlet extends HttpServlet {
	public static Logger log = Logger.getLogger(NameServlet.class.getName());
	private NameServer nameServer;

	public void service(HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws ServletException, IOException {
		HttpParameters httpParameters = Util.toHttpParameters(servletRequest);
		if (httpParameters.has("action", "getfile")) {
			handleGetFile(httpParameters, servletResponse);
			return;
		}
		PrintStringWriter printStringWriter = new PrintStringWriter();
		Response response = new Response(printStringWriter);
		try {
			if (StringUtils.equalsIgnoreCase("post", servletRequest.getMethod())) {
				OceanXML oceanRequest = OceanXML.parse(servletRequest.getReader());
				OceanXML oceanResponse = new OceanXML();
				if (httpParameters.has("action", "updatecellnodes")) {
					handleUpdateCellNodes(oceanRequest, oceanResponse);
				}
				response.contentType = ClusterConstants.CONTENT_TYPE_XML;
				Element responseElement = oceanResponse.toElement();
				XMLUtil.outputElement(responseElement, printStringWriter);
			} else if (StringUtils.equalsIgnoreCase("get", servletRequest.getMethod())) {
				String action = httpParameters.get("action");
				if (StringUtils.equals(action, "getfile")) {
					handleGetFile(httpParameters, servletResponse);
				} else if (StringUtils.equals("getlatestindexschema", action)) {
					handleGetLatestIndexSchema(httpParameters, servletResponse);
				} else {
					OceanXML oceanResponse = new OceanXML();
					if (StringUtils.isBlank(action)) {
					} else if (StringUtils.equals("getchosenmaster", action)) {
						handleGetChosenMaster(httpParameters, oceanResponse);
					} else if (StringUtils.equals("getname", action)) {
						handleGetName(httpParameters, oceanResponse);
					} else if (StringUtils.equals("notifyname", action)) {
						handleNotifyName(httpParameters, oceanResponse);
					} else if (StringUtils.equals("getdata", action)) {
						handleGetData(httpParameters, oceanResponse);
					} else if (StringUtils.equals("getcellnodes", action)) {
						handleGetCellNodes(httpParameters, oceanResponse);
					} else if (StringUtils.equals("registerfornodeevents", action)) {
						handleRegisterForNodeEvents(httpParameters, oceanResponse);
					}
				}
			}
			servletResponse.setStatus(200);
		} catch (Throwable throwable) {
			try {
				log.log(Level.SEVERE, "", throwable);
				servletResponse.setStatus(500);
				String responseXML = OceanXML.toOceanXMLString(throwable);
				printStringWriter = new PrintStringWriter();
				printStringWriter.print(responseXML);
			} catch (Throwable exTh) {
				log.log(Level.SEVERE, "", throwable);
				log.log(Level.SEVERE, "inside creating exception response", exTh);
				throw new ServletException(exTh);
			}
		} finally {
			if (printStringWriter != null) {
				servletResponse.setContentType(response.contentType);
				PrintWriter writer = servletResponse.getWriter();
				writer.write(printStringWriter.toString());
			}
		}
	}

	private void handleRegisterForNodeEvents(HttpParameters parameters, OceanXML response) throws Exception {
		String cell = parameters.get("cell");
		URL url = parameters.getUrl("url");
		Long duration = parameters.getLong("duration");
		nameServer.registerForNodeEvents(cell, url, duration);
	}

	private void handleGetCellNodes(HttpParameters parameters, OceanXML response) throws Exception {
		String cell = parameters.get("cell");
		List<Node> nodes = nameServer.getCellNodes(cell);
		response.addBodyAll(nodes);
	}

	private void handleGetData(HttpParameters parameters, OceanXML response) throws Exception {
		Long replicationid = parameters.getLong("replicationid");
		List<CElement> results = nameServer.getData(replicationid);
		for (CElement element : results) {
			response.addBody(element);
		}
	}

	private void handleNotifyName(HttpParameters parameters, OceanXML response) throws Exception {
		nameServer.syncData();
	}

	private void handleGetName(HttpParameters parameters, OceanXML response) throws Exception {
		Name name = nameServer.getName();
		response.addBody(name);
	}

	private void handleGetChosenMaster(HttpParameters parameters, OceanXML response) throws Exception {
		Name name = nameServer.getChosenMaster();
		response.addBody(name);
	}

	private void handleUpdateCellNodes(OceanXML request, OceanXML response) throws Exception {
		List<Node> nodes = new ArrayList<Node>();
		for (Element nodeElement : request.getBodyChildren()) {
			Node node = new Node(nodeElement);
			nodes.add(node);
		}
		// TODO: adjust for being in name, insert into db
		nameServer.nodesUpdated(nodes);
		nameServer.notifyNames();
	}

	private void handleGetLatestIndexSchema(HttpParameters parameters, HttpServletResponse servletResponse) {
		File filesDirectory = nameServer.getFilesDirectory();
		String schemaString = "schema.";
		long max = -1;
		for (File file : filesDirectory.listFiles()) {
			if (!file.isDirectory()) {
				String name = file.getName();
				if (name.startsWith(schemaString)) {
					String numeral = name.substring(schemaString.length(), schemaString.lastIndexOf('.') - 1);
					max = Math.max(Long.parseLong(numeral), max);
				}
			}
		}
		if (max > 0) {
			handleGetFile(new HttpParameters("name", schemaString+max+".xml"), servletResponse);
		} else {
			servletResponse.setStatus(404);
		}
	}

	private void handleGetFile(HttpParameters parameters, HttpServletResponse servletResponse) {
		try {
			File filesDirectory = nameServer.getFilesDirectory();
			String name = parameters.get("name");
			File file = new File(filesDirectory, name);
			servletResponse.setStatus(200);
			servletResponse.setContentLength((int) file.length());
			FileInputStream fileInput = new FileInputStream(file);
			OutputStream output = servletResponse.getOutputStream();
			IOUtils.copy(fileInput, output);
			fileInput.close();
		} catch (IOException ioException) {
			servletResponse.setStatus(404);
		}
	}
}
